package cn.com.cnxfs.relaxchat;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

import org.eclipse.paho.client.mqttv3.MqttException;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class ChatActivity extends AppCompatActivity {
    private int uid;
    private int friend;
    String nickname; //对方的昵称
    private MyDatabaseHelper databaseHelper;
    private LocalBroadcastManager localBroadcastManager;
    private LocalReceiver localReceiver;
    final List<String> listdata = new ArrayList<>();
    ListAdapter adapter;
    String myNickname;
    private List<BroadcastReceiver> broadcastReceivers = new ArrayList<>(); //监听下载事件

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        nickname = getIntent().getStringExtra("nickname");
        final SharedPreferences preferences = getSharedPreferences("db_login",MODE_PRIVATE);
        myNickname = preferences.getString("nickname", "隐藏的用户"); //当前登录用户的nickname
        String username = getIntent().getStringExtra("username");
        int id = getIntent().getIntExtra("id", 0);
        uid = getIntent().getIntExtra("uid", 0);
        friend = getIntent().getIntExtra("friend", 0);
        byte state1 = getIntent().getByteExtra("state", (byte) 0); //好友状态里的state，消息是否已读
        setTitle("与"+ nickname +"聊天中");
        setContentView(R.layout.activity_chat);

        databaseHelper = new MyDatabaseHelper(ChatActivity.this, "database.db", null, 1);
        Button button = findViewById(R.id.button_send_chat);
        final EditText editText = findViewById(R.id.editText_chat);
        ListView listView = findViewById(R.id.listview_chat);
        adapter = new ArrayAdapter<String>(ChatActivity.this, android.R.layout.simple_list_item_1, listdata);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String string = listdata.get(i);
                int pos = string.indexOf("http://");
                if(pos != -1){ //显示图片
                    final View view111 = getLayoutInflater().inflate(R.layout.layout_show_picture, null);
                    String imageName = string.substring(string.lastIndexOf("/")+1);
                    Log.d("!!!!!!!!!!!!", getFilesDir().getAbsolutePath() + File.separator + imageName);
                    File file = new File(getFilesDir().getAbsolutePath() + File.separator + imageName);
                    if(file.exists()) { //判断图片文件在本地是否已存在
                        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                        ImageView imageView = view111.findViewById(R.id.picShow);
                        imageView.setImageBitmap(bitmap);
                    } else { // 在本地不存在，需要去服务器上拉取并保存到本地，最后删除服务器上的文件
                        Log.d("!!!!!!", "不存在"+string.substring(pos));
                        DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(string.substring(pos)));
                        request.setDestinationInExternalFilesDir(ChatActivity.this,null, imageName);
                        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                        download_listener(downloadManager.enqueue(request), imageName, view111); //开始下载并监听下载完成事件
                    }
                    AlertDialog.Builder builder = new AlertDialog.Builder(ChatActivity.this);
                    builder.setTitle("查看图片");
                    builder.setView(view111);
                    builder.setCancelable(false);
                    builder.setNegativeButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    });
                    builder.show();
                }
            }
        });

        //读取数据库，将聊天记录显示在界面上
        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        Cursor cursor = db.query("chat",null,"friend="+friend,null,null,null,"ctime");
        if(cursor.moveToFirst()){
            do{
                String var = cursor.getString(cursor.getColumnIndex("var"));
                String ctime = cursor.getString(cursor.getColumnIndex("ctime"));
                int state = cursor.getInt(cursor.getColumnIndex("state"));
                if(state==0) //state为0表示发送，为1表示接受
                    listdata.add(ctime+" 我："+var);
                else
                    listdata.add(ctime+" Ta："+var);
            }while(cursor.moveToNext());
            ((ArrayAdapter) adapter).notifyDataSetChanged(); //刷新listview
            Log.d("chat页面","从本地数据库读取聊天记录成功！");
        }
        cursor.close();
        //再标记该好友的消息已读
        ContentValues values = new ContentValues();
        values.put("state", 1);
        db.update("friend",values,"friend="+friend,null);

        //监听广播，收到新消息进入的广播时马上显示新消息到界面上
        localReceiver = new LocalReceiver();
        localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("cn.com.cnxfs.relaxchat.NEW_CHAT");
        localBroadcastManager.registerReceiver(localReceiver, intentFilter); //注册本地广播接收器
        final Intent intent = new Intent("cn.com.cnxfs.relaxchat.UPDATE_NEW_CHAT");
        localBroadcastManager.sendBroadcast(intent); //发送本地广播通知news页面：该好友的消息已读

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String chat_str = editText.getText().toString();
                if(chat_str.equals("")){  //输入空白消息
                    Toast.makeText(ChatActivity.this, "请输入消息内容", Toast.LENGTH_SHORT).show();
                }else {
                    final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String ctime = format.format(new Date().getTime());
                    //通过MQTT连接向服务器发送消息,格式senderId|receiverId|ctime|var|senderNickname
                    byte[] temp = Base64.encode(chat_str.getBytes(), Base64.URL_SAFE);
                    Log.d("内容加密", new String(temp));
                    String payload = uid+"|"+friend+"|"+ctime+"|"+(new String(temp))+"|"+myNickname;
                    temp = Base64.encode(payload.getBytes(), Base64.URL_SAFE);
                    Log.d("消息加密", new String(temp));
                    try {
                        // 发送消息给好友
                        Api.mqtt_client.publish("user"+friend, temp, 2, false);
                        // 同时发给服务器备份
                        String swap = uid+","+friend+","+new String(temp); //uid,friend,var
                        Api.mqtt_client.publish("myMQTT/swap", swap.getBytes(), 2, false);
                        //聊天记录写入本地数据库
                        SQLiteDatabase db = databaseHelper.getWritableDatabase();
                        ContentValues values = new ContentValues(); //要添加到数据库中的数据
                        values.put("friend",friend);
                        values.put("ctime",ctime);
                        values.put("var",chat_str);
                        values.put("state",0); //发送消息
                        db.insert("chat", null, values);
                        Log.d("数据库", "发送的聊天记录存入chat表成功！");
                        //更新界面，显示新的消息
                        listdata.add(ctime+" 我："+chat_str);
                        ((ArrayAdapter) adapter).notifyDataSetChanged();
                        editText.setText("");
                        //发送本地广播，发布了新消息
                        localBroadcastManager.sendBroadcast(intent);
                    } catch (MqttException e) {
                        e.printStackTrace();
                        Toast.makeText(ChatActivity.this, "以送失败，请重试！", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

        if(nickname.contains("】")){
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("提示");
            builder.setMessage("对方不是你的好友！是否将其加为好友？若否，聊天记录将会被隐藏，只有对方下主动发来消息时才会显示！");
            builder.setCancelable(false);
            builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {  //向后端传递好友请求
                            try{
                                FormBody.Builder params = new FormBody.Builder();
                                params.add("uid", String.valueOf(uid)); //添加url参数
                                params.add("friend", String.valueOf(friend));
                                OkHttpClient client = new OkHttpClient();
                                Request request = new Request.Builder()
                                        .url(Api.url[0]+"/friend/add").post(params.build())
                                        .build();
                                Response response = client.newCall(request).execute(); //执行发送指令
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        Toast.makeText(ChatActivity.this,"好友添加成功！",Toast.LENGTH_SHORT).show();
                                    }
                                });
                            }catch (Exception e){
                                e.printStackTrace();
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        Toast.makeText(ChatActivity.this,"好友请求发送失败！请检查网络连接！",Toast.LENGTH_SHORT).show();
                                    }
                                });
                            }
                        }
                    }).start();
                }
            });
            builder.setNegativeButton("否", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            builder.show();
        }

        FloatingActionButton actionButton = findViewById(R.id.floatingActionButton);
        actionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(ContextCompat.checkSelfPermission(ChatActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!=
                        PackageManager.PERMISSION_GRANTED){ //还没有取得外部存储的权限，向用户要
                    ActivityCompat.requestPermissions(ChatActivity.this,
                            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                            1);
                }else { //已经取得权限了
                    selectPhoto();
                }
            }
        });
    }

    private void download_listener(final long Id, final String fileName, final View view){ //监听下载事件
        IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                long ID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                if (ID == Id) {
                    File sourceFile = new File(Objects.requireNonNull(getExternalFilesDir(null)).getAbsolutePath() + File.separator + fileName);
                    File destFile = new File(getFilesDir().getAbsolutePath() + File.separator + fileName);
                    try {  //将下载的图片从Android/data中APP的私人数据文件夹里拷贝到APP在手机内部存储中的APP私人数据
                        FileInputStream fileInputStream = new FileInputStream(sourceFile);
                        FileOutputStream fileOutputStream = new FileOutputStream(destFile);
                        byte[] bytes = new byte[1024];
                        int i = 0;
                        while ((i = fileInputStream.read(bytes)) > 0)
                            fileOutputStream.write(bytes, 0 ,i);
                        fileInputStream.close();
                        fileOutputStream.close();
                        sourceFile.delete(); //删除下载到APP私人数据目录下的临时文件
                        Log.d("图片下载",fileName+"下载成功！");
                        ImageView imageView = view.findViewById(R.id.picShow);
                        Bitmap bitmap = BitmapFactory.decodeFile(destFile.getAbsolutePath());
                        imageView.setImageBitmap(bitmap);
                        //通知服务器删文件
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    FormBody.Builder params = new FormBody.Builder();
                                    params.add("name", fileName);
                                    OkHttpClient client = new OkHttpClient();
                                    Request request = new Request.Builder()
                                            .url(Api.url[0] + "/photo/del")
                                            .post(params.build()).build();
                                    Response response = client.newCall(request).execute(); //执行发送指令
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }).start();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Toast.makeText(ChatActivity.this,fileName+"下载失败！",Toast.LENGTH_SHORT).show();
                        Log.d("图片下载",fileName+"下载失败！");
                    }
                }
            }
        };
        broadcastReceivers.add(broadcastReceiver);
        registerReceiver(broadcastReceiver,intentFilter);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == 1){ //获取外部存储空间权限
            for(int i=0;i<permissions.length;i++){
                if(grantResults[i]!=PackageManager.PERMISSION_GRANTED) { //用户选择了禁止
                    Toast.makeText(ChatActivity.this, "权限获取失败！本功能无法使用！", Toast.LENGTH_SHORT).show();
                    finish();
                }
            }
            selectPhoto();
        }
    }

    void selectPhoto(){ //选择图片
        Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null);
        intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        startActivityForResult(intentToPickPic, 2);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == 2){ //返回选择的照片信息
        if (data!=null)
            handleImageOnKitKat(data);
        }
    }
    
    private void handleImageOnKitKat(Intent data) {
        String imagePath = null;
        Uri uri = data.getData();
        if (DocumentsContract.isDocumentUri(this, uri)) {
            // 如果是document类型的Uri，则通过document id处理
            String docId = DocumentsContract.getDocumentId(uri);
            assert uri != null;
            if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
                String id = docId.split(":")[1];
                // 解析出数字格式的id
                String selection = MediaStore.Images.Media._ID + "=" + id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
            } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content: //downloads/public_downloads"), Long.parseLong(docId));
                imagePath = getImagePath(contentUri, null);
            }
        } else {
            assert uri != null;
            if ("content".equalsIgnoreCase(uri.getScheme())) {
                // 如果是content类型的Uri，则使用普通方式处理
                imagePath = getImagePath(uri, null);
            } else if ("file".equalsIgnoreCase(uri.getScheme())) {
                // 如果是file类型的Uri，直接获取图片路径即可
                imagePath = uri.getPath();
            }
        }
        // 根据图片路径显示图片
        uploadImage(imagePath);
    }

    private String getImagePath(Uri uri, String selection) {
        String path = null; // 通过Uri和selection来获取真实的图片路径
        Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
            cursor.close();
        }
        return path;
    }

    private void uploadImage(final String imagePath) {  //下面开始上传图片
        if (imagePath != null) {
            Log.d("图片路径", imagePath);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{ //发送POST请求给后端，上传图片
                        OkHttpClient client = new OkHttpClient(); //创建http客户端
                        final File file = new File(imagePath); //需要被上传的文件，小心权限问题
                        MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
                        RequestBody fileBody = RequestBody.create(MediaType.parse("image/*"), file); //上传的文件以及类型, image
                        requestBody.addFormDataPart("file", file.getName(), fileBody);// 参数：请求key,文件名称,RequestBody
                        Request request = new Request.Builder().url(Api.url[0] + "/photo/upload").post(requestBody.build()).build();
                        client.newBuilder().readTimeout(20000, TimeUnit.MILLISECONDS).build().newCall(request).enqueue(new Callback() {
                            @Override    // readTimeout("请求超时时间" , 时间单位);
                            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        Toast.makeText(ChatActivity.this, "图片发送失败", Toast.LENGTH_SHORT).show();
                                    }
                                });
                                Log.d("文件上传", "失败了");
                                e.printStackTrace();
                            }
                            @Override
                            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                                String imageName = response.body().string();  // response.body().string()只能用一次，多次调用会报错
                                int len = Api.url[0].length();
                                String imageURL;
                                if(Api.url[0].substring(len-1,len).equals("/"))
                                    imageURL = Api.url[0] + "photo/" + imageName; //图片上传到服务器上的地址
                                else  //补上斜线
                                    imageURL = Api.url[0] + "/photo/" + imageName; //图片上传到服务器上的地址
                                Log.d("返回值", imageURL);
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        Toast.makeText(ChatActivity.this,"图片发送成功！",Toast.LENGTH_SHORT).show();
                                    }
                                });
                                // 将图片地址发给对方
                                final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                String ctime = format.format(new Date().getTime());
                                //通过MQTT连接向服务器发送消息,格式senderId|receiverId|ctime|var|senderNickname
                                byte[] temp = Base64.encode(imageURL.getBytes(), Base64.URL_SAFE);
                                Log.d("内容加密", new String(temp));
                                String payload = uid+"|"+friend+"|"+ctime+"|"+(new String(temp))+"|"+myNickname;
                                temp = Base64.encode(payload.getBytes(), Base64.URL_SAFE);
                                Log.d("消息加密", new String(temp));
                                try {
                                    // 发送消息给好友
                                    Api.mqtt_client.publish("user"+friend, temp, 2, false);
                                    // 同时发给服务器备份
                                    String swap = uid+","+friend+","+new String(temp); //uid,friend,var
                                    Api.mqtt_client.publish("myMQTT/swap", swap.getBytes(), 2, false);
                                    //聊天记录写入本地数据库
                                    SQLiteDatabase db = databaseHelper.getWritableDatabase();
                                    ContentValues values = new ContentValues(); //要添加到数据库中的数据
                                    values.put("friend",friend);
                                    values.put("ctime",ctime);
                                    values.put("var",imageURL);
                                    values.put("state",0); //发送消息
                                    db.insert("chat", null, values);
                                    Log.d("数据库", "发送的聊天记录存入chat表成功！");
                                    //更新界面，显示新的消息
                                    listdata.add(ctime+" 我："+imageURL);
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            ((ArrayAdapter) adapter).notifyDataSetChanged();
                                        }
                                    });
                                    //发送本地广播，发布了新消息
                                    localBroadcastManager.sendBroadcast(new Intent("cn.com.cnxfs.relaxchat.UPDATE_NEW_CHAT"));
                                    // 下面将图片备份到聊天记录文件区
                                    File file1 = new File(getFilesDir().getAbsolutePath(), imageName);
                                    FileInputStream fileInputStream = new FileInputStream(file);
                                    FileOutputStream fileOutputStream = new FileOutputStream(file1);
                                    byte[] bytes = new byte[1024];
                                    int i = 0;
                                    while ((i = fileInputStream.read(bytes)) > 0)
                                        fileOutputStream.write(bytes, 0 ,i);
                                    fileInputStream.close();
                                    fileOutputStream.close();
                                    Log.d("发送图片","保存至聊天记录成功");
                                } catch (MqttException e) {
                                    e.printStackTrace();
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(ChatActivity.this, "发送失败，请重试！", Toast.LENGTH_SHORT).show();
                                        }
                                    });
                                }
                            }
                        });
                    }catch (Exception e){
                        e.printStackTrace();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(ChatActivity.this,"网络连接失败！",Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                }
            }).start();
        } else {
            Toast.makeText(this, "获取相册图片失败", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onDestroy() {  //别忘了取消注册广播接收器
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
        for(BroadcastReceiver i:broadcastReceivers)
            unregisterReceiver(i);
    }

    class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("chat页面","收到本地广播！");
            if(friend==intent.getIntExtra("friend",0)){
                //更新界面，显示新的消息，添加到数据库的操作在MQTT接收消息的进程里进行
                listdata.add(intent.getStringExtra("ctime")+" Ta："+intent.getStringExtra("var"));
                ((ArrayAdapter) adapter).notifyDataSetChanged();
            }
        }
    }
}
